package com.tarena.lbs.coupon.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.github.pagehelper.PageInfo;
import com.google.rpc.Status;
import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.attach.pojo.dto.AttachQrDTO;
import com.tarena.lbs.attach.pojo.param.AttachQRParam;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.coupon.dao.repository.ActivityRepository;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.coupon.dao.mapper.CouponMapper;
import com.tarena.lbs.coupon.dao.mapper.UserCouponsMapper;
import com.tarena.lbs.coupon.dao.repository.CouponRepository;
import com.tarena.lbs.coupon.dao.repository.StockRepository;
import com.tarena.lbs.coupon.dao.repository.UserCouponsRepository;
import com.tarena.lbs.coupon.pojo.param.CouponParam;
import com.tarena.lbs.coupon.pojo.po.CouponPO;
import com.tarena.lbs.coupon.pojo.po.UserCouponsPO;
import com.tarena.lbs.coupon.pojo.query.CouponQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponCodeQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponQuery;
import com.tarena.lbs.coupon.pojo.vo.CouponVO;
import com.tarena.lbs.coupon.pojo.vo.UserCouponsVO;
import com.tarena.lbs.coupon.service.CouponService;
import com.tarena.lbs.user.dao.repository.UserGroupRepository;
import com.tarena.lbs.user.dao.repository.UserRepository;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponServiceImpl implements CouponService {
    // 二维码有效期30分钟
    private static final int QR_CODE_EXPIRE_MINUTES = 30;
    // Redis键前缀（领取记录ID → 二维码信息）
    private static final String QR_CODE_REDIS_PREFIX = "coupon:qrcode:";
    // Redis键前缀（核销码 → 领取记录ID）
    private static final String VERIFY_CODE_REDIS_PREFIX = "coupon:verify:";

    @Resource
    private CouponMapper couponMapper;
    @Resource
    private CouponRepository couponRepository;
    @Resource
    private ActivityRepository activityRepository;
    @Resource
    private UserCouponsRepository userCouponsRepository;
    @Resource
    private ThreadPoolTaskExecutor executor;
    @DubboReference
    private UserRepository userRepository;
    @DubboReference
    private UserGroupRepository userGroupRepository;
    @Resource
    private StockRepository stockRepository;
    @Qualifier("redisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserCouponsMapper userCouponsMapper;
    @DubboReference
    private AttachApi attachApi;

    @Override
    public PageResult<CouponVO> pageList(CouponQuery query) {
        //1.创建一个新的分页结果对象，用于存储最终的分页查询结果
        PageResult<CouponVO> voPage = new PageResult<>();
        //total objects pageNo pageSize totalPage
        //2.调用 couponRepository 的 gePages 方法进行分页查询，获取包含活动持久化对象的分页信息
        PageInfo<CouponPO> pageInfo = couponRepository.pageList(query);
        //pageNo pageSize total
        //3.将分页信息中的页码设置到分页结果对象中
        voPage.setPageNo(pageInfo.getPageNum());
        // 将分页信息中的每页数量设置到分页结果对象中
        voPage.setPageSize(pageInfo.getPageSize());
        // 将分页信息中的总记录数设置到分页结果对象中
        voPage.setTotal(pageInfo.getTotal());
        // 声明一个存储管理员视图对象的列表
        List<CouponVO> vos = null;
        // 从分页信息中获取管理员持久化对象列表
        List<CouponPO> couponPOList = pageInfo.getList();
        // 判断持久化对象列表是否不为空
        if (CollectionUtils.isNotEmpty(couponPOList)) {
            vos = couponPOList.stream().map(po -> {
                CouponVO vo = new CouponVO();
                BeanUtils.copyProperties(po, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        voPage.setObjects(vos);
        return voPage;
    }

    @Override
    public CouponVO detail(Integer id) {
        CouponPO po = couponRepository.selectById(id);
        CouponVO vo = null;
        if (po != null) {
            vo = new CouponVO();
            BeanUtils.copyProperties(po, vo);
        }
        return vo;
    }


    @Override
    public void addCoupon(CouponParam couponParam) {
        CouponPO po = new CouponPO();
        BeanUtils.copyProperties(couponParam, po);
        po.setCouponName(couponParam.getCouponName());
        po.setCouponType(couponParam.getCouponType());
        po.setDiscountValue(couponParam.getDiscountValue());
        po.setMaxDiscountAmount(couponParam.getMaxDiscountAmount());
        po.setApplicable(couponParam.getApplicable());
        po.setUsageLimit(couponParam.getUsageLimit());
        po.setUsageNum(couponParam.getUsageNum());
        po.setMaxUsageLimit(couponParam.getMaxUsageLimit());
        po.setStartDate(couponParam.getStartDate());
        po.setEndDate(couponParam.getEndDate());
        po.setDescribes(couponParam.getDescribes());
        po.setBusinessId(couponParam.getBusinessId());
        po.setExclusionType(couponParam.getExclusionType());
        po.setOrderAmount(couponParam.getOrderAmount());
        po.setCreateAt(new Date());
        po.setUpdateAt(new Date());
        po.setStatus(1);
        po.setEnableStatus(1);
        couponMapper.insert(po);
    }


//    }
//        log.info("用户:{}领取优惠券成功:{}", userId, coupon.getCouponName());
//
//        userCouponsMapper.insert(userCouponsPO);
//        userCouponsPO.setUpdateAt(new Date());
//        userCouponsPO.setCreateAt(new Date());
//        userCouponsPO.setCouponCode(UUID.randomUUID().toString());
//        userCouponsPO.setShopId(param.getShopId());
//        userCouponsPO.setActivityId(activity.getId());
//        userCouponsPO.setReceiveChannel(param.getReceiveChannel());
//        userCouponsPO.setActivityId(activity.getId());
//        userCouponsPO.setCouponId(coupon.getId());
//        userCouponsPO.setUserId(userId);
//        UserCouponsPO userCouponsPO = new UserCouponsPO();
//        //创建领取记录
//        }
//            throw new BusinessException("-10","库存不足");
//        if (updated <= 0){
//        int updated = stockRepository.updateStock(coupon.getId());
//        //1.扣减库存
//    public void doReceiveCoupon(UserCouponsParam param, Integer userId, ActivityPO activity, CouponPO coupon) throws BusinessException {
//    @Override
//    /*领取优惠券服务*/
//
//    }
//        return couponPO;
//
//                new BusinessException("-9","用户领取数量已达上限"));
//        Asserts.isTrue(userCouponCount >= couponPO.getUsageLimit(),
//        Integer userCouponCount = couponRepository.countUserCoupon(userId, couponId);
//        //5.用户领取限制校验
//                new BusinessException("-8","优惠券已领完"));
//        Asserts.isTrue(stock.getUsageNum() >= stock.getMaxUsageLimit(),
//        CouponPO stock = stockRepository.countStock(couponId);
//        //4.优惠券库存校验
//                new BusinessException("-7","优惠券未启动"));
//        Asserts.isTrue(!couponPO.getEnableStatus().equals(couponPO.getStatus()),
//        //3. 优惠券状态校验
//        Asserts.isTrue(couponPO==null,new BusinessException("-6","优惠券不存在"));
//        //2. 优惠券校验
//        CouponPO couponPO = couponRepository.selectById(couponId);
//        //1. 查询优惠券信息
//            throws BusinessException {
//    private CouponPO checkUserAndCoupon(Integer couponId, Integer userId)
//    /*检验用户和优惠券*/
//    }
//        return activityPO;
//        }
//                    new BusinessException("-5", "用户人群不存在"));
//            Asserts.isTrue(checkResult != null,
//            UserGroupPO checkResult = userGroupRepository.selectById(activityPO.getTargetCustomer());
//        if (StringUtils.isNotBlank(activityPO.getTargetCustomer())) {
//        //5.用户人群校验
//                new BusinessException("-4", "活动已结束"));
//        Asserts.isTrue(now.after(activityPO.getEndDate()),
//                new BusinessException("-3", "活动未开始"));
//        Asserts.isTrue(now.before(activityPO.getStartDate()),
//        Date now = new Date();
//        //4.活动时间校验
//                new BusinessException("-2","活动未启动"));
//        Asserts.isTrue(!activityPO.getEnableStatus().equals(activityPO.getStatus()),
//        //3.活动状态校验
//        Asserts.isTrue(activityPO==null,new BusinessException("-1","活动不存在"));
//        //2. 判断活动信息是否为空
//        ActivityPO activityPO = activityRepository.selectById(activityId);
//        //1. 查询活动信息
//            throws BusinessException {
//    private ActivityPO checkUserAndActivity(Integer activityId, Integer userId)
//    /*检验用户和活动*/
//
//    }
//        }
//            throw new BusinessException();
//            log.error("用户认证解析失败", e);
//        } catch (Exception e) {
//            return userId;
//            log.debug("从JWT上下文获取用户ID: {}", userId);
//            Integer userId = AuthenticationContextUtils.getCurrentUserId();
//        try {
//        // 3. 从JWT认证上下文获取（适用于前端请求场景）
//
//        }
//            return param.getUserId();
//            log.debug("使用参数中的用户ID: {}", param.getUserId());
//        if (param.getUserId() != null) {
//        // 2. 优先使用参数中的userId（适用于消息队列等后端调用场景）
//
//        }
//            throw new BusinessException();
//        if (param == null) {
//        // 1. 参数基础校验
//    private Integer resolveUserId(UserCouponsParam param) throws BusinessException {
//     */
//     * 统一解析用户ID
//    /**
//
//}
//        }
//            }
//                redisTemplate.delete(lockKey);
//            if (locked) {
//        }finally {
//            doReceiveCoupon(param, userId, activity, coupon);
//            }
//                throw new BusinessException("-10", "操作太频繁，请稍后再重试");
//            if (!locked) {
//            locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 60, TimeUnit.SECONDS);
//        try {
//        boolean locked = false;
//        String lockKey = "coupon:receive:" + userId + ":" + coupon.getId();
//        //4.开始执行领取优惠券（添加分布式锁）
//
//        Asserts.isTrue(coupon == null, new BusinessException("-2", "优惠券校验失败"));
//        Asserts.isTrue(activity == null, new BusinessException("-2", "活动校验失败"));
//        }
//            log.error("并发失败,异常", e);
//        } catch (Exception e) {
//            coupon = couponFuture.get();
//            activity = activityFuture.get();
//        try {
//        CouponPO coupon = null;
//        ActivityPO activity = null;
//        CompletableFuture<Void> allFuture = CompletableFuture.allOf(activityFuture, couponFuture);
//        //等待两个任务都完成
//
//        }, executor);
//            }
//                throw new RuntimeException(e);
//            } catch (BusinessException e) {
//                return checkUserAndCoupon(param.getCouponId(), userId);
//            try {
//        CompletableFuture<CouponPO> couponFuture = CompletableFuture.supplyAsync(() -> {
//        //3.校验优惠券和用户是否合法,如果合法 返回优惠券对象
//
//        }, executor);
//            }
//                throw new RuntimeException(e);
//            } catch (BusinessException e) {
//                return checkUserAndActivity(param.getActivityId(), userId);
//            try {
//        CompletableFuture<ActivityPO> activityFuture = CompletableFuture.supplyAsync(() -> {
//        //2.校验活动和用户 是否合法,如果合法 返回活动对象 以活动对象是否为空 判断是否合法
//
//        log.info("用户id：{}", userId);
//        Integer userId = resolveUserId(param);
//        //1.拿到认证的userId 也是解析 认证检验
//         */
//         * 方法优化：用户身份解析、并行校验活动和优惠券 合并校验结果
//        /**
//    public void receiveCoupon(UserCouponsParam param) throws BusinessException {
//    @Override
    /*获取用户优惠券列表*/
    @Override
    public PageResult<UserCouponsVO> receiveList(UserCouponQuery couponQuery) {
    //1.创建一个新的分页结果对象，用于存储最终的分页查询结果
    PageResult<UserCouponsVO> voPage = new PageResult<>();
    //2.调用 userCouponRepository 的 gePages 方法进行分页查询，获取包含活动持久化对象的分页信息
    PageInfo<UserCouponsPO> pageInfo = userCouponsRepository.pageList(couponQuery);
    //pageNo pageSize total
    //3.将分页信息中的页码设置到分页结果对象中
    voPage.setPageNo(pageInfo.getPageNum());
    // 将分页信息中的每页数量设置到分页结果对象中
    voPage.setPageSize(pageInfo.getPageSize());
    // 将分页信息中的总记录数设置到分页结果对象中
    voPage.setTotal(pageInfo.getTotal());
    // 4.声明一个存储管理员视图对象的列表
    List<UserCouponsVO> vos = null;
    // 从分页信息中获取管理员持久化对象列表
    List<UserCouponsPO> userCouponsPOList = pageInfo.getList();
    // 5.判断持久化对象列表是否不为空
    if (CollectionUtils.isNotEmpty(userCouponsPOList)) {
        vos = userCouponsPOList.stream().map(po -> {
            UserCouponsVO vo = new UserCouponsVO();
            BeanUtils.copyProperties(po, vo);
            return vo;
        }).collect(Collectors.toList());
    }
    voPage.setObjects(vos);
    return voPage;
}

    /*获取优惠券二维码图片*/
    @Override
    public UserCouponsVO receiveDetail(UserCouponCodeQuery query) throws BusinessException {
//        //1.验证优惠券领取记录
//        UserCouponsPO po = userCouponsRepository.selectById(query.getUserId(), query.getCouponCode());
//        if (po == null){
//            throw new BusinessException("-6", "优惠券不存在");
//        }
//        // 2. 检查是否已生成过二维码(Redis查重)
//        String existingQrCode = (String) redisTemplate.opsForValue().get("coupon:qrcode:" + query.getCouponCode());
//        if (existingQrCode != null) {
//            // 3. 已生成过二维码，直接返回二维码图片路径
//            UserCouponsVO vo = new UserCouponsVO();
//            BeanUtils.copyProperties(po, vo);
//            vo.setCouponUrl(existingQrCode);
//            return vo;
//        }
//        // 4. 未生成过二维码，生成二维码图片并保存到Redis中
//        return generateQrCode(po);
//    }
//
//    private UserCouponsVO generateQrCode(UserCouponsPO po) {
//        //生成唯一核销码
//        String couponCode = UUID.randomUUID().toString();
//        //调用附件服务
//        AttachQRParam qrParam = new AttachQRParam();
//        qrParam.setContent(couponCode);
//        qrParam.setWidth(300);
//        qrParam.setHeight(300);
//        AttachQrDTO qrDTO = attachApi.generateQrCode(qrParam);
//
//        //保存二维码图片到Redis中
//        redisTemplate.opsForValue().set("coupon:qrcode:" + couponCode, qrDTO.getUrl());
//        UserCouponsVO vo = new UserCouponsVO();
//        BeanUtils.copyProperties(po, vo);
//        vo.setCouponUrl(qrDTO.getUrl());
//        vo.setCouponCode(couponCode);
//        LocalDateTime.now().plusMinutes(QR_CODE_EXPIRE_MINUTES);
//        redisTemplate.expire("coupon:qrcode:" + couponCode, QR_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
//        return vo;
        // 1. 参数校验
        validateQuery(query);

        // 2. 验证优惠券领取记录
        UserCouponsPO couponReceive = validateCouponReceive(query);

        // 3. 生成/获取二维码
        return getOrGenerateQrCode(couponReceive);
    }

    private void validateQuery(UserCouponCodeQuery query) throws BusinessException {
        if (StringUtils.isBlank(query.getCouponCode())) {
            throw new BusinessException("-15","优惠券码不能为空");
        }
        if (query.getUserId() == null) {
            throw new BusinessException("-16","用户ID不能为空");
        }
    }

    private UserCouponsPO validateCouponReceive(UserCouponCodeQuery query) throws BusinessException {
        UserCouponsPO po = userCouponsRepository.selectById(
                query.getUserId(),
                query.getCouponCode()
        );
        if (po == null) {
            throw new BusinessException("-6", "优惠券不存在或无权访问");
        }
        if (!po.getStatus().equals(Status.CODE_FIELD_NUMBER)) {
            throw new BusinessException("-7", "优惠券不可用");
        }
        return po;
    }

    private UserCouponsVO getOrGenerateQrCode(UserCouponsPO couponReceive) throws BusinessException {
        // 检查Redis缓存
        String cacheKey = buildCacheKey(Long.valueOf(couponReceive.getId()));
        QrCodeCache cached = (QrCodeCache) redisTemplate.opsForValue().get(cacheKey);

        if (cached != null) {
            return buildVoFromCache(couponReceive, cached);
        }

        // 生成新的二维码
        return generateNewQrCode(couponReceive);
    }

    private UserCouponsVO generateNewQrCode(UserCouponsPO couponReceive) throws BusinessException {
    // 生成核销码（带业务标识）
        String verifyCode = generateVerifyCode(Long.valueOf(couponReceive.getId()));

        // 调用附件服务
        AttachQrDTO qrDTO = generateQrImage(verifyCode);

        // 构建缓存数据
        QrCodeCache cache = new QrCodeCache(
                qrDTO.getUrl(),
                verifyCode,
                System.currentTimeMillis()
        );

        // 存储Redis（双Key结构）
        storeInRedis(Long.valueOf(couponReceive.getId()), verifyCode, cache);

        // 返回VO
        return buildQrCodeVo(couponReceive, qrDTO, verifyCode);
    }

    private String generateVerifyCode(Long receiveId) {
        return String.format("CPN-%s-%s-%d",
                receiveId,
                RandomStringUtils.randomAlphanumeric(8),
                System.currentTimeMillis() % 10000
        );
    }

    private AttachQrDTO generateQrImage(String verifyCode) throws BusinessException {
        try {
            AttachQRParam param = new AttachQRParam();
            param.setContent(buildQrContent(verifyCode));
            param.setWidth(300);
            param.setHeight(300);
            return attachApi.generateQrCode(param);
        } catch (Exception e) {
            log.error("生成二维码失败", e);
            throw new BusinessException("17","生成二维码失败，请重试");
        }
    }

    private void storeInRedis(Long receiveId, String verifyCode, QrCodeCache cache) {
        // 主缓存：领取记录ID → 二维码信息
        String cacheKey = buildCacheKey(receiveId);
        redisTemplate.opsForValue().set(
                cacheKey,
                cache,
                QR_CODE_EXPIRE_MINUTES,
                TimeUnit.MINUTES
        );

        // 反向索引：核销码 → 领取记录ID
        redisTemplate.opsForValue().set(
                buildVerifyCodeKey(verifyCode),
                receiveId,
                QR_CODE_EXPIRE_MINUTES,
                TimeUnit.MINUTES
        );
    }
    /**
     * 构建二维码内容（核销链接）
     */
    private String buildQrContent(String verifyCode) {
        return String.format("%s/coupon/verify?code=%s&t=%d",
                couponRepository.getVerifyBaseUrl(), // 核销基础路径
                verifyCode,                            // 核销码参数
                System.currentTimeMillis()             // 防伪时间戳
        );
    }


    // Helper methods
    private String buildCacheKey(Long receiveId) {
        return QR_CODE_REDIS_PREFIX + receiveId;
    }

    private String buildVerifyCodeKey(String verifyCode) {
        return VERIFY_CODE_REDIS_PREFIX + verifyCode;
    }

    private UserCouponsVO buildVoFromCache(UserCouponsPO po, QrCodeCache cache) {
        UserCouponsVO vo = new UserCouponsVO();
        BeanUtils.copyProperties(po, vo);
        vo.setCouponUrl(cache.getImageUrl());
        vo.setCouponCode(cache.getVerifyCode());
        vo.setExpireTime(calculateExpireTime(cache.getCreateTime()));
        return vo;
    }

    private UserCouponsVO buildQrCodeVo(UserCouponsPO po, AttachQrDTO qrDTO, String verifyCode) {
        UserCouponsVO vo = new UserCouponsVO();
        BeanUtils.copyProperties(po, vo);
        vo.setCouponUrl(qrDTO.getUrl());
        vo.setCouponCode(verifyCode);
        vo.setExpireTime(LocalDateTime.now().plusMinutes(QR_CODE_EXPIRE_MINUTES));
        return vo;
    }

    private LocalDateTime calculateExpireTime(long createTimestamp) {
        return LocalDateTime.ofInstant(
                Instant.ofEpochMilli(createTimestamp).plus(QR_CODE_EXPIRE_MINUTES, ChronoUnit.MINUTES),
                ZoneId.systemDefault()
        );
    }

    @Data
    @AllArgsConstructor
    private static class QrCodeCache {
        private String imageUrl;
        private String verifyCode;
        private long createTime;
    }
}
